package com.lemon.cloud.admin.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CreateCache;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.lemon.cloud.admin.dto.RoleDTO;
import com.lemon.cloud.admin.entity.RoleOrg;
import com.lemon.cloud.admin.entity.UserRole;
import com.lemon.cloud.admin.mapper.SysRoleMapper;
import com.lemon.cloud.admin.service.RoleApplicationService;
import com.lemon.cloud.admin.service.RoleOrgService;
import com.lemon.cloud.admin.service.SysRoleService;
import com.lemon.cloud.admin.service.UserRoleService;
import com.lemon.cloud.comm.constants.CacheConstants;
import com.lemon.cloud.comm.constants.enums.DelFlagEnum;
import com.lemon.cloud.comm.model.ResultMsg;
import com.lemon.cloud.comm.po.RoleApplication;
import com.lemon.cloud.comm.po.SysRole;
import com.lemon.cloud.core.excel.ExcelErrorMessage;
import com.lemon.cloud.security.util.SecurityUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.lemon.cloud.comm.constants.OAuth2Constant.ROLE_PREFIX;

@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {

    @Autowired
    private UserRoleService userRoleService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RoleOrgService roleOrgService;

    @Autowired
    private RoleApplicationService roleApplicationService;

    @CreateCache(name = CacheConstants.SYS_USER_ROLE)
    private Cache<Long, List<SysRole>> roleCache;


    @Override
    public List<SysRole> getRolesByUserId(Long sysUserId) {
        if (sysUserId == null) {
            return Lists.newArrayList();
        }
        return roleCache.computeIfAbsent(sysUserId, (k) -> baseMapper.selectByUserId(sysUserId));
    }

    @Override
    public void delRolePermission(Long menuId, Long roleId) {
        if (menuId == null && roleId == null) {
            return;
        }
        baseMapper.delRolePermission(menuId, roleId);
    }

    @Override
    public IPage<SysRole> getRoleByPage(Page<SysRole> page, SysRole role) {
        return baseMapper.getRoleByPage(page, role);
    }

    @Override
    public ResultMsg<SysRole> updateRole(SysRole sysRole) {
        if (sysRole == null || sysRole.getId() == null) {
            return ResultMsg.resultFail("id不能为空");
        }
        this.update(new LambdaUpdateWrapper<SysRole>()
                .eq(SysRole::getId, sysRole.getId())
                .set(StringUtils.isNotEmpty(sysRole.getName()), SysRole::getName, sysRole.getName())
                .set(StringUtils.isNotEmpty(sysRole.getCode()), SysRole::getCode, sysRole.getCode())
                .set(StringUtils.isNotEmpty(sysRole.getDescription()), SysRole::getDescription, sysRole.getDescription())
                .set(SysRole::getUpdateBy, SecurityUtils.getUser().getUsername())
                .set(SysRole::getUpdateTime, LocalDateTime.now())
        );
        return ResultMsg.resultSuccess(sysRole);
    }

    @Override
    public ResultMsg<SysRole> addRole(SysRole sysRole) {
        sysRole.setId(IdWorker.getId());
        this.save(sysRole);
        return ResultMsg.resultSuccess(sysRole);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultMsg<Boolean> deleteRole(Long id) {
        long count = userRoleService.count(new LambdaQueryWrapper<UserRole>().eq(UserRole::getRoleId, id));
        Assert.state(count == 0, "无法删除，存在用户使用该角色");

        this.update(new LambdaUpdateWrapper<SysRole>()
                .eq(SysRole::getId, id)
                .set(SysRole::getDelFlag, DelFlagEnum.DELETE.getValue())
                .set(SysRole::getUpdateBy, SecurityUtils.getUser().getUsername())
                .set(SysRole::getUpdateTime, LocalDateTime.now())
        );
        this.delRoleMenu(id);
        return ResultMsg.resultSuccess(true);
    }

    @Override
    public void delRoleMenu(Long id) {
        baseMapper.delRoleMenu(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultMsg<Boolean> saveRoleMenus(RoleDTO roleDTO) {
        baseMapper.delRoleMenu(roleDTO.getRoleId());
        if (StrUtil.isEmpty(roleDTO.getMenuIds())) {
            return ResultMsg.resultSuccess(true);
        }
        List<String> list = Lists.newArrayList(roleDTO.getMenuIds().split(StrUtil.COMMA));
        baseMapper.saveRoleMenus(roleDTO.getRoleId(), list);
        //todo 发送mq异步 更新redis的用户角色权限
        updateCachePermission(roleDTO.getRoleId(), list);
        return ResultMsg.resultSuccess();
    }

    @Override
    public List<ExcelErrorMessage> addBatch(List<SysRole> list) {
        List<ExcelErrorMessage> errorList = Lists.newArrayList();
        List<String> code = Lists.newArrayList();
        for (int i = 1; i <= list.size(); i++) {
            Set<String> errorMsg = new HashSet<>();

            SysRole r = list.get(i - 1);
            r.setId(IdWorker.getId());
            r.setCreateTime(LocalDateTime.now());
            r.setUpdateTime(LocalDateTime.now());
            r.setCreateBy(SecurityUtils.getUser().getUsername());
            r.setUpdateBy(SecurityUtils.getUser().getUsername());
            String c = r.getCode();
            code.add(c);

            if (StrUtil.isEmpty(c)) {
                errorMsg.add(String.format("%s，%s，编码为空", r.getCode(), r.getName()));
            }
            if (!c.startsWith(ROLE_PREFIX)) {
                errorMsg.add(String.format("%s，%s，角色编码必须ROLE_开头", r.getCode(), r.getName()));
            }
            if (CollectionUtil.isNotEmpty(errorMsg)) {
                errorList.add(new ExcelErrorMessage((long) i + 1, errorMsg));
            }
        }

        Long count = baseMapper.selectCount(new LambdaQueryWrapper<SysRole>().in(SysRole::getCode, code).eq(SysRole::getDelFlag, DelFlagEnum.EXIST.getValue()));
        if (count > 0) {
            errorList.add(new ExcelErrorMessage(null, Sets.newHashSet("导入失败，数据库中存在相同编码")));
        }
        if (CollectionUtil.isEmpty(errorList)) {
            baseMapper.addBatch(list);
        }
        return errorList;
    }


    /**
     * 根据角色list查询权限
     *
     * @param roleCodeList
     * @return
     */
    @Override
    public List<RoleApplication> getDSList(List<String> roleCodeList) {
        List<RoleApplication> dataList = Lists.newArrayList();
        if (CollectionUtil.isEmpty(roleCodeList)) {
            return Lists.newArrayList();
        }
        roleCodeList.forEach(code -> {
            if (StringUtils.isNotEmpty(code)) {
                RoleApplication dataScope = roleApplicationService.getDataScope(code);
                if (dataScope != null) {
                    dataList.add(dataScope);
                }
            }
        });
        return dataList;
    }

    /**
     * 配置数据权限
     *
     * @param roleData
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultMsg<Boolean> saveRoleData(List<RoleApplication> roleData) {
        roleData = roleData.stream().filter(item-> ObjectUtil.isNotNull(item.getScopeType())).collect(Collectors.toList());

        Set<Long> roleIdSet = roleData.stream().map(RoleApplication::getRoleId).collect(Collectors.toSet());

        List<SysRole> sysRoles = baseMapper.selectList(new LambdaQueryWrapper<SysRole>().in(SysRole::getId,roleIdSet).select(SysRole::getCode,SysRole::getId,SysRole::getName));
        roleApplicationService.remove(new LambdaQueryWrapper<RoleApplication>().in(RoleApplication::getRoleId, roleIdSet));
        roleOrgService.remove(new LambdaUpdateWrapper<RoleOrg>().in(RoleOrg::getRoleId, roleIdSet));
        if (CollectionUtil.isEmpty(roleData)) {
            return ResultMsg.resultSuccess();
        }
        roleApplicationService.saveBatchRoleApp(sysRoles.stream().map(SysRole::getCode).collect(Collectors.toSet()), roleData);
        List<RoleOrg> roleOrgList = Lists.newArrayList();
        roleData.forEach(item -> {
            if (CollectionUtil.isNotEmpty(item.getOrgIdList())) {
                item.getOrgIdList().forEach(o -> {
                    RoleOrg roleOrg = new RoleOrg();
                    roleOrg.setAppId(item.getAppId());
                    roleOrg.setRoleId(item.getRoleId());
                    roleOrg.setOrgId(o);
                    roleOrgList.add(roleOrg);
                });
            }
        });

        if (CollectionUtil.isNotEmpty(roleOrgList)) {
            roleOrgService.saveBatch(roleOrgList);
        }
        return ResultMsg.resultSuccess(true);
    }


    private void updateCachePermission(Long roleId, List<String> menuIds) {
        if (CollectionUtil.isEmpty(menuIds) || roleId == null) {
            return;
        }
        List<UserRole> userRoleList = userRoleService.list(new LambdaQueryWrapper<UserRole>().eq(UserRole::getRoleId, roleId));
        if (CollectionUtil.isEmpty(userRoleList)) {
            return;
        }

        //todo 发送站内消息
        roleCache.removeAll(userRoleList.stream().map(UserRole::getUserId).collect(Collectors.toSet()));

    }
}
